﻿using System;
using System.Collections.Generic;
using HK.Core.Common.Data;
using HK.Core.Extend;
using HK.Core.Logs.Protocol;
using UnityEngine;
using HLogLevel = HK.Core.Logs.Protocol.LogLevel;

namespace HK.Core.Logs.Data
{
    
    /// <summary>
    /// 日志数据
    /// </summary>
    [Serializable]
    public class LogData : DataBase, ILogData
    {
        [SerializeField]
        private long no = -1;
        /// <summary>
        /// 日志No
        /// </summary>
        public long No => no;
        
        [SerializeField]
        private HLogLevel level = HLogLevel.Off;
        /// <summary>
        /// 日志等级
        /// </summary>
        public HLogLevel Level => level;
        
        [SerializeField]
        private string log = string.Empty;
        /// <summary>
        /// 日志内容
        /// </summary>
        public string Log => log;
        
#if UNITY_EDITOR
        /// <summary>
        /// 打包日志标志位
        /// </summary>
        public bool IsBuild { get; private set; } = false;
#else
        /// <summary>
        /// 打包日志标志位
        /// </summary>
        public bool IsBuild => false;
#endif
        
        /// <summary>
        /// 有效标志位
        /// </summary>
        public override bool Valid => 0 < no && LogLevel.Off != level && !string.IsNullOrEmpty(log);

#region Pool

        /// <summary>
        /// 缓冲池Id
        /// </summary>
        public long PoolId { get; set; } = -1L;

        /// <summary>
        /// 重置Log信息
        /// </summary>
        /// <param name="iNo">日志No</param>
        /// <param name="iLevel">日志等级</param>
        /// <param name="iLog">日志</param>
        /// <param name="iIsBuild">打包日志标志位</param>
        public void Reset(long iNo, HLogLevel iLevel, string iLog, bool iIsBuild = false)
        {
            no = iNo;
            level = iLevel;
            log = iLog;
#if UNITY_EDITOR
            IsBuild = iIsBuild;
#endif
        }

#endregion
        
#region Colors

        /// <summary>
        /// 打包日志颜色- 蓝紫
        /// </summary>
        private const string BuildColor = "cyan";
        /// <summary>
        /// 错误日志颜色
        /// </summary>
        private const string ErrorColor = "red";
        /// <summary>
        /// 警告日志颜色
        /// </summary>
        private const string WarningColor = "orange";
        /// <summary>
        /// 一般日志颜色
        /// </summary>
        private const string InfoColor = "white";

#endregion
        
        /// <summary>
        /// 取得类型字体颜色
        /// </summary>
        /// <param name="iLevel">日志等级</param>
        /// <param name="iIsBuild">Build标志位</param>
        /// <returns>类型字体颜色</returns>
        private static string GetTypeColor(HLogLevel iLevel, bool iIsBuild = false)
        {
#if UNITY_EDITOR
            // 打包日志
            if (iIsBuild)
            {
                return BuildColor;
            }
#endif
            
            var typeColor = InfoColor;
            switch (iLevel)
            {
                case HLogLevel.Warning:
                    typeColor = WarningColor; 
                    break;
                case HLogLevel.Error:
                case HLogLevel.Fatal:
                    typeColor = ColorEx.ConvertColorToHexStr(ColorEx.HotPink0); 
                    break;
                default:
                    break;
            }

            return typeColor;
        }

        
        /// <summary>
        /// 分割日志处理
        /// <para>* 专门处理存在换行的日志</para>
        /// </summary>
        /// <param name="iLog">日志</param>
        /// <param name="iSplitStr">分割字符</param>
        /// <returns>日志列表</returns>
        private static List<string> SplitLogs(string iLog, string iSplitStr)
        {
            if (string.IsNullOrEmpty(iLog)) return null;
            var logs = new List<string>();
            if (string.IsNullOrEmpty(iSplitStr))
            {
                logs.Add(iLog);
                return logs;
            }

            var logTmp = iLog;
            while (true)
            {
                var index = logTmp.IndexOf(iSplitStr, StringComparison.Ordinal);
                if(-1 == index) 
                {
                    logs.Add(logTmp);
                    break;
                }
                
                var logItem = logTmp.Substring(0, index);
                logs.Add(logItem);
                logTmp = logTmp.Substring(index + iSplitStr.Length);
                
            }
            
            return logs;
        }
        
        private static readonly string SplitStr = "@HK.KH@";
        
        /// <summary>
        /// 转换成表示用的日志列表
        /// </summary>
        /// <param name="iNo">日志No</param>
        /// <param name="iLogDetail">日志详细</param>
        /// <param name="iLevel">日志等级</param>
        /// <param name="iCurTime">当前时间</param>
        /// <param name="iCostTime">花费时间</param>
        /// <param name="oNormalLogs">日志列表</param>
        /// <param name="iIsBuild">Build标志位</param>
        /// <returns>显示用日志列表</returns>
        private static List<string> ConvertDisplayLogs(
            long iNo, string iLogDetail, HLogLevel iLevel, 
            string iCurTime, long iCostTime, out List<string> oNormalLogs, bool iIsBuild = false)
        {
            oNormalLogs = new List<string>();
            if (string.IsNullOrEmpty(iLogDetail)) return null;
            var logTmp = iLogDetail;

            // 显示用日志
            logTmp = logTmp.Replace(Environment.NewLine, SplitStr);
            logTmp = logTmp.Replace("\r", SplitStr);
            logTmp = logTmp.Replace("\n", SplitStr);
            var logs = SplitLogs(logTmp, SplitStr);
            var count = logs.Count;
            if (0 >= count) return null;
            
            var logPrefix = $"{GenerateLog(iLevel)} [{iNo}]";
#if FUNCTION_TESTS
            if (0 < iCostTime)
            {
                logPrefix = $"{logPrefix} (+ {iCostTime} ms) ";
            }
#endif
            var displayCharCount = 0;
            var displayLogs = new List<string>();
            var flg = false;
            for (var idx = 0; idx < count; ++idx)
            {
                var logLine = logs[idx];
                if (string.IsNullOrEmpty(logLine))
                {
                    oNormalLogs.Add($"{iCurTime} {logPrefix}");
                    if (512 >= displayCharCount)
                    {
                        displayLogs.Add(logPrefix);
                    }
                }
                else
                {
                    oNormalLogs.Add($"{iCurTime} {logPrefix} {logLine}");

                    if (!flg)
                    {
                        displayCharCount += logLine.Length;
                        if (512 >= displayCharCount)
                        {
#if UNITY_EDITOR
                            logLine = $"<color=\"{GetTypeColor(iLevel, iIsBuild)}\">{logPrefix} {logLine}</color>";
#else
                            logLine = $"{logPrefix} {logLine}";
#endif                      
                        }
                        else
                        {
                            if (512 >= logLine.Length)
                            {
#if UNITY_EDITOR
                                logLine = $"<color=\"{GetTypeColor(iLevel, iIsBuild)}\">{logPrefix} {logLine} ...</color>";
#else
                                logLine = $"{logPrefix} {logLine}";
#endif
                            }
                            else
                            {
#if UNITY_EDITOR
                                logLine = $"<color=\"{GetTypeColor(iLevel, iIsBuild)}\">{logPrefix} {logLine.Substring(0, 512)} ...</color>";
#else
                                logLine = $"{logPrefix} {logLine.Substring(0, 512)} ...";
#endif
                            }
                            flg = true;
                        }
                        displayLogs.Add(logLine);
                    }
                }
            }

            if (512 < displayCharCount)
            {
                displayLogs.Add($"<color=\"{GetTypeColor(iLevel, iIsBuild)}\">{logPrefix} ......</color>");
            }
            
            return displayLogs;
        }
        
        /// <summary>
        /// 生成Log日志文本<BR/>
        /// * 返回数量为1:单行<BR/>
        /// * 返回数量为 > 1:多行
        /// </summary>
        /// <param name="iNo">日志No</param>
        /// <param name="iLevel">日志等级</param>
        /// <param name="iLogDetail">日志(详细)</param>
        /// <param name="iCurTime">当前时间</param>
        /// <param name="iCostTime">耗时(单位:毫秒)</param>
        /// <param name="oDisplayLog">显示用的Log文本(富文本格式)</param>
        /// <param name="iIsBuild">Build标志位</param>
        /// <returns>Log日志文本</returns>
        public static List<string> GenerateLog(
            long iNo, HLogLevel iLevel, string iLogDetail, 
            string iCurTime, long iCostTime, out string oDisplayLog, 
            bool iIsBuild = false)
        {
            oDisplayLog = null;
            // 转换显示用Logs
            var logsTmp = ConvertDisplayLogs(
                iNo, iLogDetail, iLevel, iCurTime, iCostTime, 
                out var oNormalLogs, iIsBuild);
            if (0 >= logsTmp.Count)
            {
                oNormalLogs.Clear();
#if FUNCTION_TESTS
                if (0 >= iCostTime)
                {
                    oNormalLogs.Add(GenerateLog(iLevel, $"{iCurTime} [{iNo}] {GenerateLog(iLevel, iLogDetail)}", iIsBuild));
                }
                else
                {
                    oNormalLogs.Add(GenerateLog(iLevel, $"{iCurTime} [{iNo}] (+ {iCostTime} ms) {GenerateLog(iLevel, iLogDetail)}", iIsBuild));
                }
#else
                oNormalLogs.Add(GenerateLog(iLevel, $"{iCurTime} [{iNo}] {GenerateLog(iLevel, iLogDetail)}", iIsBuild));
#endif
                return oNormalLogs;
            }
            
            if (0 < logsTmp.Count)
            {
                oDisplayLog = $@"{string.Join(Environment.NewLine, logsTmp.ToArray())}";
            }
            
            return oNormalLogs;
        }
        
        /// <summary>
        /// 生成Log日志文本
        /// </summary>
        /// <param name="iLevel">日志等级</param>
        /// <param name="iLogDetail">日志(详细)</param>
        /// <param name="iIsBuild">打包日志标志位</param>
        /// <returns>Log日志文本</returns>
        private static string GenerateLog(HLogLevel iLevel, string iLogDetail = null, bool iIsBuild = false)
        {
#if UNITY_ANDROID
            if (Application.isMobilePlatform)
            {
                return iLogDetail;
            }
#endif
            var logTag = "";
            switch (iLevel)
            {
                case HLogLevel.Debug:
                    logTag = "[T]"; 
                    break;
                case HLogLevel.Warning:
                    logTag = "[W]"; 
                    break;
                case HLogLevel.LInfo:
                    logTag = "[LI]"; 
                    break;
                case HLogLevel.Error:
                    logTag = "[E]"; 
                    break;
                case HLogLevel.Fatal:
                    logTag = "[F]"; 
                    break;
                default:
                    logTag = "[RI]";
                    break;
            }
            logTag = iIsBuild ? $"{logTag}[B]" : $"{logTag}"; 
            
            return string.IsNullOrEmpty(iLogDetail) ? $"{logTag}" : $"{logTag} {iLogDetail}";
        }

        /// <summary>
        /// 清空
        /// </summary>
        public override void Clear()
        {
            base.Clear();
            
            no = -1L;
            level = HLogLevel.Off;
            log = null;
            
        }
    }
}
